package main

import (
	"embed"
	"encoding/json"
	"fmt"
	"github.com/spf13/cobra"
	"github.com/wuzfei/go-helper/path"
	"github.com/zeebo/errs"
	"go.uber.org/zap"
	"onvif_server/internal/migration"
	"onvif_server/internal/server"
	"onvif_server/internal/service"
	"onvif_server/internal/utils"
	"onvif_server/pkg/cfgstruct"
	"onvif_server/pkg/db"
	"onvif_server/pkg/jwt"
	"onvif_server/pkg/log"
	"onvif_server/pkg/process"
	"onvif_server/pkg/server/http"
	"onvif_server/pkg/storage"
	"onvif_server/pkg/subscribe"
	"onvif_server/pkg/version"
	"onvif_server/pkg/zlmedia_sdk"
	"onvif_server/wire"
	"os"
	"path/filepath"
	"time"
)

//go:generate stringer -type ErrCode -linecomment ./internal/errcode

//go:embed web/dist/*
var web embed.FS

type Config struct {
	Timezone     string `help:"当前时区" default:"Asia/Shanghai"`
	Api          http.Config
	Db           db.Config
	JWT          jwt.Config
	Log          log.Config
	Service      service.Config
	Task         server.Config
	ZlMedia      zlmedia_sdk.Config
	Subscribe    subscribe.Config
	LocalStorage storage.LocalConfig
	Discovery    server.DiscoveryConfig
}

var (
	cfg          Config
	migrationCfg struct {
		Admin migration.Config
		Db    db.Config
		Log   log.Config
	}
)

var (
	configFile string
	rootCmd    = &cobra.Command{
		Use:   "onvif_server",
		Short: "配置平台",
	}
	runCmd = &cobra.Command{
		Use:   "run",
		Short: "运行",
		RunE:  cmdRun,
	}
	configCmd = &cobra.Command{
		Use:   "config",
		Short: "查看当前所有配置",
		RunE:  cmdConfig,
	}
	setupCmd = &cobra.Command{
		Use:         "setup",
		Short:       "初始化配置",
		RunE:        cmdSetup,
		Annotations: map[string]string{"type": "setup"},
	}
	migrationCmd = &cobra.Command{
		Use:   "migration",
		Short: "数据库迁移初始化等操作,可选[tables|admin|default]",
		Args:  cobra.ExactArgs(1),
		RunE:  cmdMigration,
	}
	versionCmd = &cobra.Command{
		Use:   "version",
		Short: "查看版本信息",
		RunE:  cmdVersion,
	}
)

func main() {
	defaultConfig := path.ApplicationDir("onvif_server", process.DefaultCfgFilename)
	cfgstruct.SetupFlag(rootCmd, &configFile, "config", defaultConfig, "配置文件")
	//根据环境读取默认配置
	defaults := cfgstruct.DefaultsFlag(rootCmd)
	//当前程序所在目录
	currentDir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	rootDir := cfgstruct.ConfigVar("ROOT", currentDir)
	//设置系统的HOME变量
	envHome := cfgstruct.ConfigVar("HOME", os.Getenv("HOME"))
	rootCmd.AddCommand(runCmd)
	rootCmd.AddCommand(configCmd)
	rootCmd.AddCommand(setupCmd)
	rootCmd.AddCommand(migrationCmd)
	rootCmd.AddCommand(versionCmd)
	process.Bind(runCmd, &cfg, defaults, cfgstruct.ConfigFile(configFile), envHome, rootDir)
	process.Bind(configCmd, &cfg, defaults, cfgstruct.ConfigFile(configFile), envHome, rootDir)
	process.Bind(setupCmd, &cfg, defaults, cfgstruct.ConfigFile(configFile), envHome, cfgstruct.SetupMode(), rootDir)
	process.Bind(migrationCmd, &migrationCfg, defaults, cfgstruct.ConfigFile(configFile), envHome, rootDir)
	process.Bind(versionCmd, &struct{}{}, defaults)
	process.Exec(rootCmd)
}

// cmdRun 运行
func cmdRun(cmd *cobra.Command, args []string) (err error) {
	ctx, _ := process.Ctx(cmd)
	_log := log.NewLog(&cfg.Log)
	loc, err := time.LoadLocation(cfg.Timezone)
	if err != nil {
		panic(err)
	}
	deviceId := utils.GetDeviceNo()
	if deviceId == "" {
		fmt.Println("无法启动，未获取到该设备id,请检查！")
		return
	}
	cfg.Service.Endpoint = cfg.Api.Endpoint
	cfg.Service.Addr = cfg.Api.Addr
	cfg.Service.Port = cfg.Api.Port
	cfg.Service.StorageRoot = cfg.LocalStorage.Root
	cfg.Service.StorageUrl = cfg.LocalStorage.Url
	app, fn, err := wire.NewWire(ctx, deviceId, loc, _log, &web, &cfg.Db, &cfg.Api, &cfg.JWT, &cfg.Task, &cfg.ZlMedia, &cfg.Subscribe, &cfg.Service, &cfg.Discovery)
	if err != nil {
		return err
	}
	defer fn()
	if err = app.Run(ctx); err != nil {
		_log.Error("app.Run error", zap.Error(err))
	}
	return nil
}

// cmdSetup 初始化数据库
func cmdSetup(cmd *cobra.Command, args []string) error {
	return process.SaveConfig(cmd, configFile)
}

// cmdVersion 查看版本信息
func cmdVersion(cmd *cobra.Command, args []string) error {
	fmt.Println(version.Build.String())
	return nil
}

// cmdConfig 查看系统配置
func cmdConfig(cmd *cobra.Command, args []string) error {
	fmt.Printf("当前运行环境：[%s]\n", cfgstruct.DefaultsType())
	fmt.Println("当前配置文件路径：", configFile)
	output, _ := json.MarshalIndent(cfg, "", " ")
	fmt.Println(string(output))
	return nil
}

// cmdMigration 数据库迁移初始化
func cmdMigration(cmd *cobra.Command, args []string) error {
	_log := log.NewLog(&migrationCfg.Log)
	db, err := db.NewDB(&migrationCfg.Db, _log)
	if err != nil {
		return err
	}
	fmt.Println("运行数据库[", migrationCfg.Db.Driver, "]：", migrationCfg.Db.Dsn)
	mr := migration.NewMigration(&migrationCfg.Admin, _log.Named("migration"), db)
	switch args[0] {
	case "tables":
		return mr.InitTables()
	case "admin":
		return mr.InitAdminAccount()
	case "default":
		return errs.Combine(mr.InitTables(), mr.InitAdminAccount())
	}
	return fmt.Errorf("arg[%s] error", args[0])
}
